home *** CD-ROM | disk | FTP | other *** search
/ Mac Magazin/MacEasy 34 / Mac Magazin and MacEasy Magazine CD - Issue 34.iso / Grafik & Text / Alpha ƒ / Tcl / Modes / fortranMode.tcl < prev    next >
Text File  |  1997-04-11  |  21KB  |  676 lines

  1. #=============================================================================
  2. # Fortran mode definition and support procs
  3. #
  4. # Features:
  5. # 1.  Keyword colorization (slightly customizable)
  6. # 2.  Fortran-sensitive shift right/left preserve columns 1-6
  7. # 3.  Auto-indentation
  8. # 4.  Line-breaking with Ctl-Opt-J (a la emacs)
  9. # 5.  Subroutine indexing
  10. # 6.  Cmd-double-click subroutine and include-file lookup
  11. # 7.  Customizable comment and continuation characters
  12. #
  13. #------------------------------------------------------------------------------
  14. # Author: Tom Pollard <pollard@chem.columbia.edu>
  15. #
  16. # To Do:  work around grep failure for Unix-format tag files
  17. #
  18. #  4/97 - Coloring bug fixed.
  19. #  1/96 - FortMarkFile no longer marks F90 "end subroutine ..." statements
  20. #         more F90 keywords (will they never cease?)
  21. #  1/96 - user-selectable comment and continuation characters
  22. #         complete F90 keyword set (Thomas Bewley <bewley@rayleigh.stanford.edu>) 
  23. #         F90 functions and comparison operators optionally colorized ( " " )
  24. #         more complete set of C preprocessor commands colorized
  25. #         fixed case-sensitivity problem in line-indent routines
  26. #  1/96 - minor FortDblClick bug fix
  27. # 12/95 - more complete keyword set for F90 and HPF (from Tom Scavo)
  28. # 12/95 - cpp keyword colorization (George Nurser <g.nurser@soc.soton.ac.uk>)
  29. #         cmd-dbl-click supports cpp #include now
  30. # 11/95 - added FortBreakLine
  31. #         fixed case-sensitivity bug
  32. # 10/95 - fixed Cmd-Dbl-Click handler to deal w/ new(?) tag file format and
  33. #            improve performance (fortFindSub)
  34. #  9/95 - fixed getFortPrev bug with numbered lines
  35. #       - shiftLeft/Right revert to normal behavior on ill-formatted lines
  36. #  8/95 - auto-indentation is finally speedy and robust
  37. #  5/95 - added Cmd-Dbl-Click handler
  38. #       - added auto-indentation
  39. # 12/94 - fixed funcExpr, FortMarkFile search expressions
  40. #       - changed comment character from 'C' to 'c' (should be case-insensitive!)
  41. #       - added 'include' keyword
  42. #       - added FortShiftRight and FortShiftLeft procs
  43. #------------------------------------------------------------------------------
  44.  
  45.  
  46. #================================================================================
  47. if {$startingUp} {
  48.     addMode Fort dummyFort {*.f *.inc *.INC *.fcm *.for *.FOR *.f9 *.f90 *.hpf } {}
  49.     return
  50. }
  51.  
  52.  
  53.  
  54. proc dummyFort {} {}
  55.  
  56. newModeVar Fort sortedIsDefault    {0} 1
  57. newModeVar Fort wordWrap        {0}    1
  58. newModeVar Fort funcExpr    {^[^cC*!][ \t]*(subroutine|[ \ta-z*0-9]*function|entry).*$} 0
  59. newModeVar Fort autoMark        {0}    1
  60. newModeVar Fort electricTab        {1}    1
  61.  
  62. # newModeVar Fort    prefixString    {c}    0
  63. newModeVar Fort    continueChar    {$}    0
  64. newModeVar Fort    commentChar    {c}    0
  65. newModeVar Fort    colorFuncs    {0}    1
  66. newModeVar Fort    colorOpers    {0}    1
  67.  
  68. newModeVar Fort indentComment    {0}    1
  69. newModeVar Fort markTag            {{}} 0
  70.  
  71. #=============================================================================
  72. # Colorize Fortran keywords
  73. #
  74. proc fortColorKeywords {{color blue} {comment red} {specialChars black}} {
  75.     global FortmodeVars
  76.  
  77.     set FortKeywords { 
  78.         allocatable allocate assign backspace block call character close common 
  79.         complex contains continue cycle data deallocate dimension do double else 
  80.         elseif end enddo endfile endif entry equivalence exit external extrinsic 
  81.         forall format function goto if implicit include inquire integer intent 
  82.         interface intrinsic logical module namelist nullify open optional 
  83.         parameter pause pointer precision print private program public pure read 
  84.         real recursive return rewind save sequence stop subroutine target then 
  85.         use where while write assignment case default elsewhere endfile go none 
  86.         operator procedure select to type
  87.     }
  88.     
  89.     if {$specialChars != "black"} {
  90.         regModeKeywords -e $FortmodeVars(commentChar) -c $comment -k $color Fort $FortKeywords  -i {=}  -i {*}  -i {/}  -i {+}  -i {-}  -i {,}  -i {(} -i {)} -I $specialChars
  91.     } else {
  92.         regModeKeywords -e $FortmodeVars(commentChar) -c $comment -k $color Fort $FortKeywords  
  93.     }
  94.     unset FortKeywords
  95.  
  96. #=============================================================================
  97. # Colorize selected C preprocessor keywords
  98. #
  99. proc fortColorCPP {{color green}} {
  100.     set CPPKeywords  {
  101.         #if #endif #include #else #define #undef #ifdef #ifndef
  102.     }
  103.     regModeKeywords -a  -k $color Fort $CPPKeywords
  104.     unset CPPKeywords
  105. }
  106.  
  107.  
  108. #=========================================================================
  109. # Colorize Fortran operators
  110. #
  111. proc fortColorOpers {{color green}} {
  112.     set FortOperators {
  113.         eq ne lt le gt ge not and or eqv neqv true false
  114.     }
  115.     regModeKeywords -a -k $color Fort $FortOperators
  116.     unset FortOperators
  117. }
  118.  
  119. #=========================================================================
  120. # Colorize Fortran function keywords
  121. #
  122. proc fortColorFuncs {{color green}} {
  123.     # Fortran bit functions
  124.     #
  125.     set BitKeywords {
  126.         bit_size btest iand ibclr ibits ibset ieor ior ishft ishftc mvbits not
  127.     }
  128.     regModeKeywords -a -k $color Fort $BitKeywords
  129.     unset BitKeywords
  130.     
  131.     # Fortran intrinsic functions
  132.     #
  133.     set IntrinsicKeywords {
  134.         abs acos aimag asin atan atan2 conjg cos cosh dble dim dprod exp ichar 
  135.         len lge lgt lle llt log log10 max min mod sign sin sinh sqrt tan tanh 
  136.         iabs dabs cabs dacos dint dnint dasin datan datan2 dcos ccos dcosh idim 
  137.         ddim dexp cexp ifix idint alog ddlog clog alog10 dlog10 max0 amax0 max1 
  138.         amax1 dmax1 min0 amin0 min1 amin1 dmin1 amod dmod idnint float sngl 
  139.         isign dsign dsin csin dsinh dsqrt csqrt dtan dtanh aint anint char cmplx 
  140.         index int nint achar adjustl adjustr all allocated any associated 
  141.         bit_size btest ceiling count cshift date_and_time digits dot_product 
  142.         eoshift epsilon exponent floor fraction huge iachar iand ibclr ibits 
  143.         ibset ieor ior ishft ishftc kind lbound len_trim logical matmul 
  144.         maxexponent maxloc maxval merge minexponent minloc minval modulo mvbits 
  145.         nearest not pack precision present product radix random_number 
  146.         random_seed range repeat reshape rrspacing scale scan selected_int_kind 
  147.         selected_real_kind set_exponent shape size spacing spread sum 
  148.         system_clock tiny transfer transpose trim ubound unpack verify
  149.     }
  150.     regModeKeywords -a -k $color Fort $IntrinsicKeywords
  151.     unset IntrinsicKeywords    
  152. }
  153.  
  154. fortColorKeywords blue red magenta
  155. fortColorCPP green
  156. if {$FortmodeVars(colorFuncs)} {
  157.     fortColorFuncs green
  158. }
  159. if {$FortmodeVars(colorOpers)} {
  160.     fortColorOpers green
  161. }
  162. #=============================================================================
  163. # Special Fortran keybindings
  164. #
  165. bind '\[' <c>  FortShiftLeft Fort
  166. bind '\[' <co> FortShiftLeftSpace Fort
  167. bind '\]' <c>  FortShiftRight Fort
  168. bind '\]' <co> FortShiftRightSpace Fort
  169.  
  170. bind '\t'       doATab Fort
  171. bind '\t' <o>     {doATab 1} Fort
  172. bind '\t' <z>     {doATab 1} Fort
  173.  
  174. bind 'j'  <zo> FortBreakLine Fort
  175.  
  176. trace variable FortmodeVars(commentChar) w shadowFort
  177. trace variable FortmodeVars(colorFuncs) w shadowFort
  178. trace variable FortmodeVars(colorOpers) w shadowFort
  179.  
  180. #=============================================================================
  181. # Update colorization when Fortran mode variables are changed
  182. #
  183. proc shadowFort {name1 name2 op} {
  184.     global HOME FortmodeVars
  185.     if {$name1 == "FortmodeVars" && $op == "w"} {
  186.         switch $name2 {
  187.             "colorFuncs"    {
  188.                 if {$FortmodeVars(colorFuncs)} {
  189.                     fortColorFuncs green
  190.                 } else {
  191.                     fortColorFuncs black
  192.                 }
  193.              }
  194.             "colorOpers"    {
  195.                 if {$FortmodeVars(colorOpers)} {
  196.                     fortColorOpers green
  197.                 } else {
  198.                     fortColorOpers black
  199.                 }
  200.              }
  201.             "commentChar" {    
  202.                 fortColorKeywords blue red magenta
  203.             }
  204.             default {
  205.                 return
  206.             }
  207.         }
  208.     }
  209. }
  210.  
  211. #=============================================================================
  212. #
  213. proc FortMarkFile {} {
  214.     global FortmodeVars
  215.     set tag [quoteExpr2 $FortmodeVars(markTag)]
  216.     
  217.     set pat0 {^.*(subroutine|.*function|entry|program).*$}
  218.     set pat1 {^[^cC*!]([ \ta-z*0-9]*)(subroutine|.*function|entry|program)[ \t]+([a-z0-9_]+)}
  219.     set end [maxPos]
  220.     set pos 0
  221.     while {![catch {search -s -f 1 -r 1 -m 0 -i 1 $pat1 $pos} mtch]} {
  222.         regexp -nocase $pat1 [eval getText $mtch] allofit valtyp subtyp name
  223.         set start [lineStart [lindex $mtch 0]]
  224.         set next [nextLineStart $start]
  225.         set pos $next
  226.         if {! [regexp -nocase "end" $valtyp mtch]} {
  227.             set inds([lineStart $start]) $name
  228.         }
  229.         
  230.     }
  231.     
  232.     set pat2 "^(c+${tag})\[ \t\]*(\[^\n\r\]*\[^ \t\])\[^ \t\]*\$"
  233.     set pos 0
  234.     while {![catch {search -s -f 1 -r 1 -m 0 -i 1 $pat2 $pos} mtch]} {
  235.         regexp -nocase $pat2 [eval getText $mtch] allofit cc comment
  236.         regsub -all {[\/\(\)]} $comment {} comment
  237.         set start [lindex $mtch 0]
  238.         set end [nextLineStart $start]
  239.         set pos $end
  240.         set inds([lineStart $start]) $comment
  241.     }
  242.     
  243.     if {[info exists inds]} {
  244.         foreach f [lsort -integer [array names inds]] {
  245.             set next [nextLineStart $f ]
  246.             setNamedMark $inds($f) $f $f $f
  247.         }
  248.     }
  249. }
  250.  
  251. #================================================================================
  252. # Block shift left and right for Fortran mode (preserves cols 1-6)
  253. #================================================================================
  254.  
  255. proc FortShiftLeft {} {
  256.     global shiftChar
  257.     doFortShiftLeft "\t"
  258.     
  259. }
  260. proc FortShiftLeftSpace {} {
  261.     global shiftChar
  262.     doFortShiftLeft " "
  263. }
  264.  
  265. proc doFortShiftLeft {shiftChar} {
  266.     set start [lineStart [getPos]]
  267.     set end [nextLineStart [expr [selEnd] - 1]]
  268.     if {$start >= $end} {set end [nextLineStart $start]}
  269.     
  270.     set text [split [getText $start [expr $end - 1]] "\r"]
  271.     
  272.     set textout ""
  273.     
  274.     set pat {^([cC]|[ 0-9][ 0-9][ 0-9][ 0-9][ 0-9].| *[0-9]*\t)(.*)$}
  275.     foreach line $text {
  276.         if {[regexp $pat $line mtch pref body]} {
  277.             if {[string index $body 0] == $shiftChar} {
  278.                 lappend textout $pref[string range $body 1 end]
  279.             } else {
  280.                 lappend textout $line
  281.             }
  282.  
  283.         } elseif {[string index $line 0] == $shiftChar} {
  284.             lappend textout [string range $line 1 end]
  285.  
  286.         } else {
  287.             lappend textout $line
  288.         }
  289.     }
  290.  
  291.     set text [join $textout "\r"]    
  292.     replaceText $start [expr $end - 1] $text
  293.     select $start [expr 1 + $start + [string length $text]]
  294. }
  295.  
  296. proc FortShiftRight {} {
  297.     global shiftChar
  298.     doFortShiftRight "\t"
  299.     
  300. }
  301. proc FortShiftRightSpace {} {
  302.     global shiftChar
  303.     doFortShiftRight " "
  304. }
  305.  
  306. proc doFortShiftRight {shiftChar} {
  307.     set start [lineStart [getPos]]
  308.     set end [nextLineStart [expr [selEnd] - 1]]
  309.     if {$start >= $end} {set end [nextLineStart $start]}
  310.     
  311.     set text [split [getText $start [expr $end - 1]] "\r"]
  312.     
  313.     set textout ""
  314.     
  315.     set pat {^([cC]|[ 0-9][ 0-9][ 0-9][ 0-9][ 0-9].| *[0-9]*\t)(.*)$}
  316.     foreach line $text {
  317.         if {[regexp $pat $line mtch pref body]} {
  318.             lappend textout $pref$shiftChar$body
  319.         } else {
  320.             lappend textout $shiftChar$line
  321.         }
  322.     }
  323.     
  324.     set text [join $textout "\r"]    
  325.     replaceText $start [expr $end - 1] $text
  326.     select $start [expr 1 + $start + [string length $text]]
  327. }
  328.  
  329. proc FortBreakLine {} {
  330.     global FortmodeVars
  331.     set pos [getPos]
  332.     set line [getText [lineStart $pos] [expr [nextLineStart $pos]-1]]
  333.     if {[regexp {^[cC*!]} $line char]} {
  334.         insertText "\n$char "
  335.     } else {
  336.         set char $FortmodeVars(continueChar)
  337.         insertText "\n     $char"
  338.     }
  339.     FortindentLine
  340. }
  341.  
  342. #=============================================================================
  343. # Cmd-double-clicking opens include files, jumps to subroutine definitions,
  344. # and follows tags.
  345. #
  346. proc FortDblClick {from to} {
  347.     global tagFile
  348.     set pat1 {^[^cC*!][ \ta-z*0-9]*(subroutine|.*function|entry)[ \t]+}
  349.     set incPat {^[^cC*!][ \t]*include[ \t]*['"]([^'"]+)['"]}
  350.  
  351.     # First check whether an 'include' was clicked
  352.     set line [getText [lineStart $from] [expr [nextLineStart $to] - 1]]
  353.     if {[regexp -nocase $incPat $line allofit fname]} {
  354.         set path [absolutePath $fname]
  355.         if {[catch {openFileQuietly $path}]} { 
  356.             message "include file \'$fname\' not found in source folder"
  357.         }
  358.         return
  359.     }
  360.     
  361.     select $from $to
  362.     set text [getSelect]
  363.     
  364.     # First check current file for subroutine definition,...
  365.     if {![catch {fortFindSub $text} mtch]} { 
  366.         regexp -nocase $pat1 [eval getText $mtch] allofit subtyp name
  367.         pushMark
  368.         display [lindex $mtch 0]
  369. #         eval select $mtch
  370.         message "press <Ctl .> to return to original cursor position"
  371.  
  372.     # ...then check tags file.
  373.     } else {
  374.         message "Searching tags file..."
  375.         set lines [grep "^$text'" $tagFile]
  376.         if {[regexp {'(.*)'} $lines dummy fname]} { 
  377.             pushMark
  378.             if {[string match "*$fname*" [winNames -f]]} {
  379.                 bringToFront $fname
  380.             } else {
  381.                 edit $fname
  382.             }
  383.             set inds [fortFindSub $text]
  384. #             set inds [search -s -f 1 -r 1 -i 1 "$pat1$text" 0]
  385.             display [lindex $inds 0]
  386. #             eval select $inds
  387.             message "press <Ctl .> to return to original cursor position"
  388.         }
  389.     }
  390. }
  391.  
  392. # Speedy search for a Fortran subroutine.  Performance is dramatically 
  393. # improved by scanning for the name alone first, rather than running 
  394. # complicated regexp search on the entire file.
  395. #
  396. proc fortFindSub {name} {
  397.     set pat1 {^[^cC*!][ \ta-z*0-9]*(subroutine|.*function|entry)[ \t]+}
  398.     set pos 0
  399.     while {![catch {search -s -f 1 -r 0 -m 0 -i 1 $name $pos} mtch]} {
  400.         set beg [lineStart [lindex $mtch 0]]
  401.         set end [expr [nextLineStart [lindex $mtch 1]] -1]
  402.         set line [getText $beg $end]
  403.         if {[regexp  -nocase $pat1$name $line allofit subtyp name]} {
  404.             return $mtch 
  405.         } else {
  406.             set pos [lindex $mtch 1]
  407.         }
  408.     }
  409.     error "Subroutine \"$name\" not found"
  410. }
  411.  
  412. #=============================================================================
  413. # Fortan auto-indentation
  414. #
  415. # Logic:
  416. #    0.    Identify previous line
  417. #            a) ignore comments and continuation lines
  418. #            b) if current line is a CONTINUE that matches a DO, use the
  419. #                first corresponding DO as the previous line
  420. #
  421. #    1.    Find leading whitespace for previous line
  422. #
  423. #    2.    Increase whitespace if previous line starts a block, i.e.,
  424. #            a) DO loop
  425. #            b) IF ... THEN 
  426. #            c) ELSE
  427. #
  428. #    3.    Decrease whitespace if current line ends a block, i.e.,
  429. #            a) ELSE || ENDIF || END IF || ENDDO || END DO
  430. #            b) <linenum> CONTINUE matching a preceding DO
  431. #
  432. #        or if previous line ends a DO loop on an executable statement, i.e.,
  433. #            c) <linenum> (not CONTINUE) matching a preceding DO
  434. #
  435. ####################################################################################
  436. # Fortan auto-indentation
  437. #
  438. proc FortindentLine {} {    
  439.     set bol [lineStart [getPos]]
  440.     set eol [expr [nextLineStart $bol] - 1]
  441.     Fortindent $bol $eol
  442. }
  443.  
  444. proc FortindentRegion {} {    
  445.     Fortindent [getPos] [selEnd]
  446. }
  447.  
  448. ####################################################################################
  449. # Fortan auto-indentation of a specified region
  450. #
  451. proc Fortindent {pos0 pos1} {
  452.     global fortDooz fortPrevLine fortTop msg
  453.     global FortmodeVars
  454.  
  455.     set tag [quoteExpr2 $FortmodeVars(markTag)]
  456.     set doComment $FortmodeVars(indentComment)
  457.  
  458.     # Define regexps
  459.     set subPat {^[^cC*!][ \ta-z*0-9]*(subroutine|.*function|entry|program)[ \t]+([a-z0-9_]+)}
  460.     set bolPat {^[^cC*!\n\r][ \t]*[^ \t\n\r][^\r\n]*$}
  461.     set mtPat {^[ \t]*$}
  462.     set tab "    "
  463.     
  464.     set contPat {^     ([^ \t\n\r])[^\r\n]*$}
  465.     set lnumPat {^([ \t]*)([0-9]*)([ \t]*)(.*)$}
  466.     set comPat "^(\[cC*!\]+(${tag})?)(\[ \t\]*)(.*)\$"
  467.     set doPat {^[^cC*!\n\r][ \t]*do[ \t]+}
  468.     set tailPat {[^\r\n]*$}
  469.     
  470.     set bobPat {^(if[^\n\r]*then|else|do)}
  471.     set eobPat {^(end[ \t]*if|end[ \t]*do|else)}
  472.     set enddoPat {^(end[ \t]*do|continue)}
  473.     
  474. #     set fortTop [fortSubTop $pos0]
  475.     set fortTop -1
  476.     
  477.     catch {unset fortDooz}
  478.     set fortPrevLine ""
  479.     
  480.     # Loop over region line by line
  481.     set from [lindex [posToRowCol $pos0] 0]
  482.     set to [lindex [posToRowCol $pos1] 0]
  483.     
  484.     while {$from <= $to} {        
  485.         set msg "Indenting line $from"
  486.         message $msg
  487.         set bol [lineStart [rowColToPos $from 0]]
  488.         set eol [expr [nextLineStart $bol] - 1]
  489.         set thisLine [getText $bol $eol]
  490.         goto $bol
  491.         
  492.         # Check whether we're entering a new routine
  493.         #
  494.         if {[regexp $subPat $thisLine allofit subType subName]} {
  495.             # alertnote "entering subr: \/$subName\/"
  496.             set fortTop $bol
  497.             catch {unset fortDooz}
  498.         } 
  499.         
  500.         # Is the current line a comment line...
  501.         #        
  502.         if {[regexp $comPat $thisLine allofit cc tag pre body]} {
  503.             if {$FortmodeVars(indentComment) > 0} {
  504.                 set body [string trimright $body]
  505.                 # alertnote "comment line: \/$pre\/$body\/"
  506.                 set lwhite "$cc     "
  507.                 
  508.                 replaceText $bol $eol $lwhite$body
  509.             }
  510.             
  511.         # ... or a line of code (possibly empty)?
  512.         #    
  513.         } elseif {[regexp $lnumPat $thisLine allofit pre lnum post body]} {
  514.             set body [string trimright $body]
  515.             # alertnote "line: \/$pre\/$lnum\/$post\/$body\/"
  516.             
  517.             # is it a continuation line?
  518.             #
  519.             if {(![regexp {\t} $pre]) && ([string length $pre] == 5)} {
  520.                 set cont [string index $lnum$post$body 0]
  521.                 set body [string trimleft [string range $lnum$post$body 1 end]]
  522.             } else {
  523.                 set cont {}
  524.             }
  525.             # alertnote "cont: \/$cont\/"
  526.             
  527.             # get whitespace for preceding line
  528.             set enddo [getFortPrev $bol $lnum]
  529.             set lwhite [getFortLwhite $bol]
  530.             
  531.             # if this line ends a block, decrease the whitespace
  532.             if {[regexp $eobPat $body] || ($enddo && [regexp -nocase $enddoPat $body])} {
  533.                 set lwlen [expr [string length $lwhite] - 4]
  534.                 set lwhite [string range $lwhite 0 $lwlen]
  535.             } 
  536.             
  537.             if {[string length $lnum]} {
  538.                 if {[string index $lwhite 0] != $tab} {
  539.                     set lwhite [string range $lwhite [expr [string length $lnum] +1] end]
  540.                 }
  541.                 set lnum " $lnum"
  542.             }
  543.             # alertnote "lwhite: \/$lwhite\/ len: [string length $lwhite]"
  544.             # message "$msg : replacing text      "
  545.             
  546.             if {[string length $cont]} {
  547.                 replaceText $bol $eol "     $cont$lwhite$body"    
  548.             } else {
  549.                 replaceText $bol $eol $lnum$lwhite$body
  550.                 if {[string length $body] > 0} {
  551.                     set fortPrevLine $lnum$lwhite$body
  552.                 }
  553.             }
  554.         } else {
  555.             # message "$msg : Couldn't parse line         "
  556.         }
  557.         
  558.         # message "$msg : Done                "
  559.         incr from
  560.     }
  561. }
  562.  
  563. proc getFortLwhite {bol} {
  564.     global fortDooz fortPrevLine fortTop msg
  565.     # Define regexps
  566.     set tab "    "
  567.     set lnumPat {^([ \t]*)([0-9]*)([ \t]*)(.*)$}
  568.     set doPat {^[^cC*!\n\r][ \t]*do[ \t]+}
  569.     set bobPat {^(if[^\n\r]*then|else|do)}
  570.     set enddoPat {^(end[ \t]*do|continue)}
  571.     
  572.     if {[regexp $lnumPat $fortPrevLine allofit pre0 lnum0 post0 body0]} {
  573.         # alertnote "prevLine: \/$pre0\/$lnum0\/$post0\/$body0\/"
  574.         
  575.         if {[string length $lnum0]} {
  576.             if {[string index $post0 0] == $tab} {
  577.                 set lwhite $post0
  578.             } else {
  579.                 regsub -all {[0-9]} $pre0$lnum0$post0 { } lwhite
  580.             }
  581.         } else {
  582.             set lwhite $pre0
  583.         }
  584.         # alertnote "lwhite: \/$lwhite\/ len: [string length $lwhite]"
  585.         # message "$msg : got lwhite (initial)"
  586.         
  587.         # if there's a line number and it's not a CONTINUE or ENDDO, 
  588.         # then check for a matching DO statement and adjust 
  589.         # indentation if found
  590.         #
  591.         if {[string length $lnum0] && ![regexp -nocase $enddoPat $body0]} {
  592.             if {[getFortPrev [lineStart [expr $bol - 1]] $lnum0]} {
  593.                 set lwlen [expr [string length $lwhite] - 4]
  594.                 set lwhite [string range $lwhite 0 $lwlen]
  595.  
  596.             }
  597.         }
  598.         
  599.         # If the preceeding line begins a block (IF-THEN, DO, or ELSE),
  600.         # then increase the whitespace
  601.         #    
  602.         if {[regexp -nocase $bobPat $body0]} {
  603.             set lwhite "$lwhite   "
  604.             
  605.             if {[regexp -nocase "$doPat\(\[0-9\]+\)" $body0 mtch donum]} {
  606.                 set eol [expr [nextLineStart $bol] - 1]
  607.                 set fortDooz($donum) [getText $bol $eol]
  608.             }
  609.         }
  610.         # message "$msg : got lwhite (final)  "
  611.     }
  612.     return "$lwhite"
  613. }
  614.  
  615. proc getFortPrev {bol lnum} {        
  616.     global fortDooz fortPrevLine fortTop msg
  617.     # Define regexps
  618.     set doPat {^[^cC*!\n\r][ \t]*do[ \t]+}
  619.     set bolPat {^[^cC*!\n\r][ \t]*[^ \t\n\r][^\r\n]*$}
  620.     set contPat {^     ([^ \t\n\r])[^\r\n]*$}
  621.  
  622.     # if there's a line number, check for a matching DO statement ...
  623.     if {[string length $lnum]} {
  624.         if {[lsearch [array names fortDooz] $lnum] >= 0} {
  625.             set fortPrevLine $fortDooz($lnum)
  626.             return 1
  627.         } else {
  628.             if {$fortTop < 0} {
  629.                 set fortTop [fortSubTop $bol]
  630.             }
  631.             if {![catch {search -s -f 0 -r 1 -i 1 -l $fortTop $doPat$lnum [expr $bol -1]} dolst]} {
  632.                 set fortPrevLine [eval getText $dolst]
  633.                 set fortDooz($lnum) $fortPrevLine
  634.                 # alertnote "doLine0: \/$fortPrevLine\/"
  635.                 return 1
  636.             }
  637.         }
  638.     }
  639.         
  640.     # ... otherwise find the first preceding non-comment, non-continuation line
  641.     if {[string length $fortPrevLine] == 0} {
  642.         if {[catch {
  643.             set lst [search -s -f 0 -r 1 -i 1 -s $bolPat [expr $bol-1]]
  644.             set fortPrevLine [eval getText $lst]
  645.             while {[regexp -nocase $contPat $fortPrevLine]} {
  646.                 set lst [search -s -f 0 -r 1 -i 1 $bolPat [expr [lindex $lst 0] - 1]]
  647.                 set fortPrevLine [eval getText $lst]
  648.             }
  649.         }]} {
  650.             # if search fails, we're at the top of a file, so reset indentation
  651.             set fortPrevLine "      continue"
  652.         }
  653.     }
  654.     
  655.     # alertnote "prevLine: \/$fortPrevLine\/"
  656.     # message "$msg : got prevLine"
  657.     return 0
  658. }
  659.  
  660. # Find the beginning of the current subroutine
  661. #
  662. proc fortSubTop {{pos 0}} {
  663.     if {$pos == 0} {
  664.         set pos [lineStart [getPos]]
  665.     }
  666.     set subPat {^[^cC*!][ \ta-z*0-9]*(subroutine|.*function|entry|program)[ \t]+([a-z0-9_]+)}
  667.     
  668.     if {![catch {search -s -f 0 -r 1 -m 0 -i 1 $subPat $pos} sublst]} {
  669.         # set subLine [eval getText $sublst]
  670.         # alertnote "subLine: \/$subLine\/"
  671.         return [lindex $sublst 0]
  672.     } else {
  673.         return 0
  674.     } 
  675. }